__author__ = 'JackSong'
import socket
import errno
from tornado.ioloop import IOLoop
from tornado.gen import coroutine

_ERRNO_WOULDBLOCK = (errno.EWOULDBLOCK, errno.EAGAIN)


class IOloop(object):
    def __init__(self, ioloop):
        self.ioloop = ioloop
        self.bridges = {}

    @staticmethod
    def instance():
        if not hasattr(IOloop, "_instance"):
            IOloop._instance = IOloop(IOLoop.instance())
        return IOloop._instance

    def register(self, bridge):
        self.bridges[bridge.fd] = bridge
        self.add_handler(bridge.fd)

    def add_handler(self, fd):
        self.ioloop.add_handler(fd, self.read_data_callback, self.ioloop.READ | self.ioloop.ERROR)

    def update_handler(self, fd, events):
        self.ioloop.update_handler(fd, events)

    def remove_handler(self, fd):
        self.ioloop.remove_handler(fd)
        bridge = self.bridges.pop(fd, None)
        if bridge: del bridge

    @coroutine
    def read_data_callback(self, fd, event):
        if event & IOLoop.READ:
            bridge = self.bridges.get(fd, None)
            if bridge:
                data = ''
                while True:
                    try:
                        recv = yield bridge.executor(bridge.shell.recv, *(1024,))
                        data = ''.join((data, recv,))
                        if len(recv) == 0:
                            break
                    except socket.error as e:
                        if e.errno == errno.EAGAIN:
                            self.update_handler(fd, IOLoop.WRITE)
                        elif isinstance(e, socket.timeout):
                            break
                        else:
                            self.remove_handler(fd)
                if data:
                    rest = yield bridge.executor(bridge.trans_back, *(data,))
                    if not rest:
                        self.remove_handler(fd)
            else:
                return

        if event & IOLoop.ERROR:
            self.remove_handler(fd)
